package com.googlecode.uipipedream.spec;

import java.awt.Color;
import java.util.Observable;

/**
 * Represents an aproximation of a colour selection.
 * @author <a href="http://daemonsadvocate.blogspot.com/">Matthew B. Gray</a>
 * @version 1.0 (27/10/2008 @ 12:57:40 AM)
 */
public class ColourModel extends Observable {
	// Outrigger variable declarations.
	private float offset = 1f/2; // scalar
	private int nColours = 3;
	// Primary colour variable declarations.
	private float brightness = (float) 1.0;
	private float hue = (float) 0.0;
	private float saturation = (float) 0.0;
	
	// Outrigger controls.
	public float getOffset() { return offset; }
	public int getNColours() { return nColours; }
	
	public void setOffset(float newOffset) {
		float max = 1f/this.nColours;
		
		// Normalize inputs.
		if(max < newOffset)
			newOffset = max;
		else if(newOffset < 0f)
			newOffset = 0f;
		
		this.offset = newOffset;
		setChanged();
	}
	
	public void setNColours(int colours) {
		// Normalize inputs.
		if(colours%2==0)
			colours = colours - 1;
		if(colours<1)
			colours = 1;
		
		// Update offset.
		float max = 1f/colours;
		if(max < this.offset)
			this.offset = max;
		
		// Update colours.
		this.nColours = colours;
		setChanged();
	}
	// Primary colour controls.
	public void setColor(Color c) { setRGB(c.getRed(), c.getGreen(), c.getBlue()); }
	public Color getColor() { return Color.getHSBColor(hue, saturation, brightness); }
	public float getHue() { return hue; }
	public float getSaturation() { return saturation; }
	public float getBrightness() { return brightness; }
	public void setBrightness(float brightness) {
		if (brightness < 0.0) brightness = 0f;
		if (brightness >= 1.0) brightness = 1f;
		this.brightness = brightness;
		setChanged();
	}
	public void setHue(float hue) {
		if (hue < 0.0) hue = 0f;
		if (hue >= 1.0) hue = 0.999f;
		this.hue = hue;
		setChanged();
	}
	public void setSaturation(float saturation) {
		if (saturation < 0.0) saturation = 0f;
		if (saturation >= 1.0) saturation = 1f;
		this.saturation = saturation;
		setChanged();
	}
	public void setRGB(int r, int g, int b) {
		if (r < 0) r = 0;
		if (r > 255) r = 255;
		if (g < 0) g = 0;
		if (g > 255) g = 255;
		if (b < 0) b = 0;
		if (b > 255) b = 255;
		float[] vals = new float[3];
		Color.RGBtoHSB(r, g, b, vals);
		this.hue = vals[0];
		this.saturation = vals[1];
		this.brightness = vals[2];
		setChanged();
	}
	
	public float getComplementaryHue() { return (0.5f+hue) % 1f; }
	public float[] getAnalogusHues() {
		final float[] toReturn = new float[nColours];
		final int halfN = nColours/2; // halfN = 5/2 = 2
		
		for (int i=halfN; 0<i; i--) { // generates i=1
			// 1. Setup internal stuff.
			final float incrementalOffset = offset * i;
			final float leftOutrigger = (hue - incrementalOffset) % 1f;
			final float rightOutrigger = (hue + incrementalOffset) % 1f;
			
			// 2. Set returning values.
			toReturn[halfN - i] = leftOutrigger;
			toReturn[halfN + i] = rightOutrigger;
		}
		toReturn[halfN] = hue;
		return toReturn;
	}
	
	public float[] getSplitComplementHues() {
		final float[] toReturn = new float[nColours];
		final int halfN = nColours/2; // halfN = 5/2 = 2
		
		for (int i=halfN; 0<i; i--) { // generates i=1
			// 1. Setup internal stuff.
			final float incrementalOffset = offset * i;
			final float leftOutrigger = (this.getComplementaryHue() - incrementalOffset) % 1f;
			final float rightOutrigger = (this.getComplementaryHue() + incrementalOffset) % 1f;
			
			// 2. Set returning values.
			toReturn[halfN - i] = leftOutrigger;
			toReturn[halfN + i] = rightOutrigger;
		}
		toReturn[halfN] = hue;
		return toReturn;
	}
	
	protected void setChanged() {
		super.setChanged();
		notifyObservers();
	}
	
	// Outrigger Brainstorm.
	/**@deprecated*/ public float getSplitComplementaryHue1() { return (getComplementaryHue() + offset) % 1f; }
	/**@deprecated*/ public float getSplitComplementaryHue2() { return (getComplementaryHue() - offset) % 1f; }
	/**@deprecated*/ public float getTriadHue1() { return (1f/3 + hue) % 1f; }
	/**@deprecated*/ public float getTriadHue2() { return ((2f/3) + hue) % 1f; }
	/**@deprecated*/ public float getAnalogHue1() { return (hue + offset) % 1f; }
	/**@deprecated*/ public float getAnalogHue2() { return (hue - offset) % 1f; }
}